package cc.wsyw126.java.whatisjava.ThinkingInUnsafe;

import cc.wsyw126.java.model.User;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashSet;

/**
 * Created by junpeng.yang on 16/12/28.
 */
public class ThinkingInUnsafe {
    /**
     * 这个会报错
     * 在使用Unsafe之前，我们需要创建Unsafe对象的实例。
     * 这并不像Unsafe unsafe = new Unsafe()这么简单，因为Unsafe的构造器是私有的。
     * 它也有一个静态的getUnsafe()方法，但如果你直接调用Unsafe.getUnsafe()，你可能会得到SecurityException异常。
     * 只能从受信任的代码中使用这个方法。
     */
//    private static final Unsafe unsafe = Unsafe.getUnsafe();
    public Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        return unsafe;
    }

    private void testInstance() throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        Unsafe unsafe = getUnsafe();
        A a = (A) unsafe.allocateInstance(A.class);
        a.getA();
        System.out.println("---------------");

        A a1 = A.class.newInstance();
        a1.getA();

        System.out.println("---------------");
        A a2 = new A();
        a2.getA();
    }

    private void testGuard() throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        Guard guard = new Guard();
        System.out.println("guard.giveAccess() = " + guard.giveAccess());
        System.out.println("-------------------");

        Unsafe unsafe = getUnsafe();
        Field f = guard.getClass().getDeclaredField("ACCESS_ALLOWED");
        unsafe.putInt(guard, unsafe.objectFieldOffset(f), 42);
        System.out.println("guard.giveAccess() = " + guard.giveAccess());
        System.out.println("-------------------");


    }

    public long sizeOf(Object o) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        Unsafe u = getUnsafe();
        HashSet<Field> fields = new HashSet<>();
        Class c = o.getClass();
        while (c != Object.class) {
            for (Field f : c.getDeclaredFields()) {
                if ((f.getModifiers() & Modifier.STATIC) == 0) {
                    fields.add(f);
                }
            }
            c = c.getSuperclass();
        }

        // get offset
        long maxSize = 0;
        for (Field f : fields) {
            long offset = u.objectFieldOffset(f);
            System.out.println("f.getName() = " + f.getName());
            System.out.println("offset = " + offset);
            if (offset > maxSize) {
                maxSize = offset;
            }
        }

        return ((maxSize / 8) + 1) * 8;   // padding
    }

    public long sizeOfObject(Object object) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        return getUnsafe().getAddress(
                normalize(getUnsafe().getInt(object, 4L)) + 12L);
    }

    private long normalize(int value) {
        if (value >= 0) return value;
        return (~0L >>> 32) & value;
    }

    public static void main(String[] args) throws Exception {
        ThinkingInUnsafe thinkingInUnsafe = new ThinkingInUnsafe();
        thinkingInUnsafe.testInstance();

        System.out.println("-------------------");

        thinkingInUnsafe.testGuard();


        long l = thinkingInUnsafe.sizeOf(new User());
        System.out.println("l = " + l);


//        long l1 = thinkingInUnsafe.sizeOfObject(new User());
//        System.out.println("l1 = " + l1);
    }
}
